package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	"gopkg.in/validator.v2"
	"log"
	"net/http"
	"strconv"
)

type App struct {
	Router *mux.Router
	Middleware *Middleware
	Config *Env
}

type ShortenReq struct {
	URL string `json:"url" validate:"nonzero"`
	ExpirationInMinutes string `json:"expiration_in_minutes" validate:"min=0"`
}

type ShortlinkResp struct {
	Shortlink string `json:"shortlink"`
}

func (a * App) Initialize(e *Env) {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	a.Config = e
	a.Router = mux.NewRouter()
	a.Middleware = &Middleware{}
	a.InitializeRoutes()
}

func (a * App) InitializeRoutes() {
	m := alice.New(a.Middleware.LoggingHandle, a.Middleware.RecoverHandle)
	a.Router.Handle("/api/shorten", m.ThenFunc(a.createShortlink)).Methods("POST")
	a.Router.Handle("/api/info", m.ThenFunc(a.getShortlinkInfo)).Methods("GET")
	a.Router.Handle("/{shortlink:[a-zA-Z0-9]{1,11}}", m.ThenFunc(a.redirect)).Methods("GET")

}

func (a * App) createShortlink(w http.ResponseWriter, r *http.Request) {
	req := &ShortenReq{}
	err := json.NewDecoder(r.Body).Decode(req)
	if err != nil {
		respondWithError(w, StatusError{http.StatusBadRequest, fmt.Errorf("parase parameters faild %v", r.Body)})
		return
	}
	err = validator.Validate(req)
	if err != nil {
		respondWithError(w, StatusError{http.StatusBadRequest, fmt.Errorf("validate parameters faild %v", req)})
		return
	}
	defer r.Body.Close()
	int64, err := strconv.ParseInt(req.ExpirationInMinutes, 10, 64)
	s, err := a.Config.S.Shorten(req.URL, int64)
	if err != nil {
		respondWithError(w, err)
	} else {
		respondWithJson(w, http.StatusCreated, ShortlinkResp{
			Shortlink: s,
		})
	}
}

func (a * App) getShortlinkInfo(w http.ResponseWriter, r *http.Request) {
	vals := r.URL.Query()
	s := vals.Get("shortlink")
	d, err := a.Config.S.ShortlinkInfo(s)
	if err != nil {
		respondWithError(w, err)
	} else {
		respondWithJson(w, http.StatusOK, d)
	}
}

func (a * App) redirect(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	//fmt.Printf("%s\n", vars["shortlink"])
	u, err := a.Config.S.Unshorten(vars["shortlink"])
	if err != nil {
		respondWithError(w, err)
	} else {
		http.Redirect(w, r, u, http.StatusTemporaryRedirect)
	}
}

func (a * App) Run(addr string) {
	log.Fatal(http.ListenAndServe(addr, a.Router))
}

func respondWithError(w http.ResponseWriter, err error)  {
	switch e := err.(type) {
	case Error:
		log.Printf("HTTP %d - %s", e.Status(), e)
		respondWithJson(w, e.Status(), e.Error())
	default:
		respondWithJson(w, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
	}
}

func respondWithJson(w http.ResponseWriter, code int, payload interface{})  {
	resp, _ := json.Marshal(payload)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	w.Write(resp)
}